package org.wu.framework.lazy.orm.database.lambda.stream.condition.part;

import org.wu.framework.core.NormalUsedString;
import org.wu.framework.core.exception.AssertFactory;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.lazy.orm.core.persistence.conf.LazyTableEndpoint;
import org.wu.framework.lazy.orm.core.persistence.conf.LazyTableFieldEndpoint;
import org.wu.framework.lazy.orm.core.persistence.util.LazySQLUtil;
import org.wu.framework.lazy.orm.core.source.SqlSourceClass;
import org.wu.framework.lazy.orm.core.source.adapter.SourceFactory;
import org.wu.framework.lazy.orm.database.lambda.domain.Persistence;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * description sql 段落 抽象接口
 *
 * @author Jia wei Wu
 * @date 2022/12/28 10:31 上午
 */
public abstract class SqlPartAbstract implements SqlPartExecutedAdapter {


    /**
     * 执行类型
     */
    abstract Persistence.ExecutionEnum getExecutionEnum();

    /**
     * 列字段对应的映射
     *
     * @return
     */
    abstract Map<String, String> getColumnAsMap();


    /**
     * 主表
     */
    abstract String getPrimaryTable();

    /**
     * 主表 class
     */
    abstract Class getPrimaryClass();

    /**
     * 链表
     */
    abstract List<SqlPart> getJoinConditions();

    /**
     * or 查询
     */
    abstract List<SqlPart> getOrSqlPart();

    /**
     * 获取 upsert 中的字段数据
     */
    abstract List<HashMap<String/*column*/, Object/*value*/>> getInsert();

    /**
     * 获取 upsert 中的ON DUPLICATE KEY UPDATE
     */
    abstract Map<String/*id*/, Object/*values (id)*/> getInsertDuplicateKey();

    /**
     * 获取 upsert 中的字段数据
     *
     * @return
     */
    abstract Map<String, Object> getInsertSet();

    /**
     * 获取Update 中的set
     *
     * @return
     */
    abstract Map<String, Object> getUpdateSet();

    /**
     * 忽略映射的列
     *
     * @return
     */
    abstract List<String> getIgnoreColumnList();

    /**
     * 仅仅使用as（忽略其他的字段）
     *
     * @return
     */
    abstract Boolean getOnlyUseAs();

    /**
     * 获取查询片段
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder select() {
        AssertFactory.isFalse(Persistence.ExecutionEnum.SELECT.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");
        // select %s.* from %s
        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.SELECT.getExecution());

        SqlSourceClass sqlSourceClass = SqlSourceClass.getInstance(getPrimaryClass());
        LazyTableEndpoint<? extends LazyTableFieldEndpoint> lazyTableEndpoint = sqlSourceClass.getLazyTableEndpoint();

//        LazyTableEndpoint lazyTableEndpoint = LazyTableUtil.analyzeLazyTable(getPrimaryClass());
        Map<String, String> columnAsMap = new HashMap<>();
        if (!getOnlyUseAs()) {
            String primaryTable = getPrimaryTable();
            Map<String, String> masterTablesColumnsAsMap = lazyTableEndpoint
                    .getLazyTableFieldEndpointList()
                    .stream()
                    .collect(
                            Collectors.toMap(fieldLazyTableFieldEndpoint -> primaryTable + NormalUsedString.DOT + fieldLazyTableFieldEndpoint.getColumnName(), LazyTableFieldEndpoint::getAlias)
                    );
            columnAsMap.putAll(masterTablesColumnsAsMap);
            // 添加 join 表中的字段
            if (!ObjectUtils.isEmpty(getJoinConditions())) {
                // 去除链表重复字段 优先级 master 第一 后续表出现的顺序
                Map<String, String> joinTablesColumnsAsMap = getJoinConditions().stream().map(conditions -> {
                            Class<?> entityClass = conditions.getPrimaryClass();
                            SqlSourceClass joinSqlSourceClass = SqlSourceClass.getInstance(entityClass);
                            LazyTableEndpoint joinClassLazyTableEndpoint = joinSqlSourceClass.getLazyTableEndpoint();
//                            LazyTableEndpoint joinClassLazyTableEndpoint = LazyTableUtil.analyzeLazyTable(entityClass);
                            List<LazyTableFieldEndpoint> fieldEndpoints = joinClassLazyTableEndpoint.getLazyTableFieldEndpointList();
                            String fullTableName = joinClassLazyTableEndpoint.getFullTableName();
                            return fieldEndpoints.stream()
                                    .collect(Collectors.toMap(fieldLazyTableFieldEndpoint -> fullTableName + NormalUsedString.DOT + fieldLazyTableFieldEndpoint.getColumnName(), LazyTableFieldEndpoint::getAlias));
                        }).filter(joinTableColumnSql -> !ObjectUtils.isEmpty(joinTableColumnSql)).flatMap(m -> m.entrySet().stream())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (A, B) -> B));
                columnAsMap.putAll(joinTablesColumnsAsMap);
            }
            columnAsMap.putAll(getColumnAsMap());
        } else {
            columnAsMap = getColumnAsMap();
        }
        columnAsMap = columnAsMap.entrySet().stream()
                .filter(stringStringEntry -> !getIgnoreColumnList().contains(stringStringEntry.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (A, B) -> B))
                // K-V  ---》 V-K   去重
                .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, (A, B) -> {
                    // 优先选取 as map 中的key
                    if (getColumnAsMap().containsKey(A)) {
                        return A;
                    } else if (getColumnAsMap().containsKey(B)) {
                        return B;
                    }
                    return B;
                }))
                // 去重
                // V-K  ---》 K-V
                .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, (A, B) -> B));
        // 添加as
        if (!ObjectUtils.isEmpty(columnAsMap)) {
            String as = columnAsMap.entrySet().stream().map(stringStringEntry -> NormalUsedString.SPACE + stringStringEntry.getKey() + NormalUsedString.SPACE + NormalUsedString.AS + NormalUsedString.SPACE + stringStringEntry.getValue() + NormalUsedString.SPACE).collect(Collectors.joining(NormalUsedString.COMMA));

            sqlPartStringBuilder.append(as);
        } else {
            sqlPartStringBuilder.append(NormalUsedString.ASTERISK);
        }
        String primaryFullTableName = lazyTableEndpoint.getFullTableName();
        // 添加 from
        sqlPartStringBuilder
                .append(NormalUsedString.FROM)
                .append(primaryFullTableName);
        return sqlPartStringBuilder;
    }

    /**
     * 获取更新片段
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder update() {
        // update sys_user set id_deleted=false where id >1
        AssertFactory.isFalse(Persistence.ExecutionEnum.UPDATE.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");

        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.UPDATE.getExecution())
                .append(getPrimaryTable())
                .append(NormalUsedString.SET);
        if (!ObjectUtils.isEmpty(getUpdateSet())) {
            String setSql = getUpdateSet().entrySet().stream().map(stringStringEntry -> {
                String columnName = stringStringEntry.getKey();
                Object fieldValue = stringStringEntry.getValue();

                return columnName + NormalUsedString.EQUALS + LazySQLUtil.valueToSqlValue(fieldValue);
            }).collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(setSql);
        }
        return sqlPartStringBuilder;
    }

    /**
     * 插入数据  insert  into  sys_user(id) values(1)
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder insert() {
        // insert  into  sys_user(id) values(1)
        AssertFactory.isFalse(Persistence.ExecutionEnum.INSERT.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");

        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.INSERT.getExecution())
                .append(getPrimaryTable())
                .append(NormalUsedString.LEFT_BRACKET);
        // 添加列
        String columns = String.join(NormalUsedString.COMMA, getInsert().getFirst().keySet());
        sqlPartStringBuilder.append(columns);
        sqlPartStringBuilder.append(NormalUsedString.RIGHT_BRACKET);

        // 添加value
        sqlPartStringBuilder.append(NormalUsedString.VALUES);
        String rowsValue = getInsert().stream().map(stringObjectHashMap -> NormalUsedString.LEFT_BRACKET +
                stringObjectHashMap.values().stream().map(o -> {
                    if (ObjectUtils.isEmpty(o)) {
                        return null;
                    } else {
                        return o.toString();
                    }
                }).collect(Collectors.joining(NormalUsedString.COMMA))
                + NormalUsedString.RIGHT_BRACKET).collect(Collectors.joining(NormalUsedString.COMMA));
        sqlPartStringBuilder.append(rowsValue);
        return sqlPartStringBuilder;
    }

    /**
     * 插入数据  insert  into  sys_user(id) values(1) ON DUPLICATE KEY UPDATE password=values (password),username=values (username)
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder upsert() {
        //  insert  into  sys_user(id) values(1) ON DUPLICATE KEY UPDATE password=values (password),username=values (username)
        AssertFactory.isFalse(Persistence.ExecutionEnum.UPSERT.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");

        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.UPSERT.getExecution())
                .append(getPrimaryTable())
                .append(NormalUsedString.LEFT_BRACKET);
        // 添加列
        String columns = String.join(NormalUsedString.COMMA, getInsert().getFirst().keySet());
        sqlPartStringBuilder.append(columns);
        sqlPartStringBuilder.append(NormalUsedString.RIGHT_BRACKET);

        // 添加value
        sqlPartStringBuilder.append(NormalUsedString.VALUES);
        String rowsValue = getInsert().stream().map(stringObjectHashMap -> NormalUsedString.LEFT_BRACKET +
                stringObjectHashMap.values().stream().map(o -> {
                    if (ObjectUtils.isEmpty(o)) {
                        return null;
                    } else {
                        return o.toString();
                    }
                }).collect(Collectors.joining(NormalUsedString.COMMA))
                + NormalUsedString.RIGHT_BRACKET).collect(Collectors.joining(NormalUsedString.COMMA));
        sqlPartStringBuilder.append(rowsValue);
        // 添加 ON DUPLICATE KEY UPDATE
        sqlPartStringBuilder.append(NormalUsedString.SPACE);
        sqlPartStringBuilder.append(NormalUsedString.ON_DUPLICATE_KEY_UPDATE);

        Map<String, Object> insertDuplicateKey = getInsertDuplicateKey();
        if (ObjectUtils.isEmpty(insertDuplicateKey)) {
            // 使用结构
            // ON DUPLICATE KEY UPDATE password=values (password),username=values (username)
            String duplicateKey = getInsert()
                    .getFirst()
                    .keySet()
                    .stream()
                    .map(key ->
                            NormalUsedString.SPACE + key + NormalUsedString.SPACE + NormalUsedString.EQUALS + NormalUsedString.SPACE + NormalUsedString.VALUES + NormalUsedString.LEFT_BRACKET + key + NormalUsedString.RIGHT_BRACKET)
                    .collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(duplicateKey);
        } else {
            // 使用 insertDuplicateKey

            String duplicateKey = insertDuplicateKey
                    .entrySet()
                    .stream()
                    .map(stringObjectEntry -> NormalUsedString.SPACE + stringObjectEntry.getKey() + NormalUsedString.SPACE + NormalUsedString.EQUALS + stringObjectEntry.getValue())
                    .collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(duplicateKey);
        }
        return sqlPartStringBuilder;
    }

    /**
     * 插入数据
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder insertWithSet() {

        // insert  into  sys_user set id_deleted=false
        AssertFactory.isFalse(Persistence.ExecutionEnum.INSERT_WITH_SET.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");

        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.INSERT_WITH_SET.getExecution())
                .append(getPrimaryTable())
                .append(NormalUsedString.SET);
        if (!ObjectUtils.isEmpty(getInsertSet())) {
            String setSql = getInsertSet().entrySet().stream().map(stringStringEntry -> {
                String columnName = stringStringEntry.getKey();
                Object fieldValue = stringStringEntry.getValue();

                return SourceFactory.cleanSpecialColumn(columnName) + NormalUsedString.EQUALS + LazySQLUtil.valueToSqlValue(fieldValue);
            }).collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(setSql);
        }
        return sqlPartStringBuilder;
    }

    /**
     * 获取插入片段
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder upsertWithSet() {
        AssertFactory.isFalse(Persistence.ExecutionEnum.UPSERT_WITH_SET.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");

        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.UPSERT_WITH_SET.getExecution())
                .append(getPrimaryTable());
        sqlPartStringBuilder.append(NormalUsedString.SPACE);


        if (!ObjectUtils.isEmpty(getInsertSet())) {
            sqlPartStringBuilder.append(NormalUsedString.LEFT_BRACKET);
            // 设置字段
            String setColumnSql = getInsertSet().keySet().stream().map(SourceFactory::cleanSpecialColumn).collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(setColumnSql);
            sqlPartStringBuilder.append(NormalUsedString.RIGHT_BRACKET);
            sqlPartStringBuilder.append(NormalUsedString.SPACE);

            // 设置value
            sqlPartStringBuilder.append(NormalUsedString.VALUES);
            sqlPartStringBuilder.append(NormalUsedString.SPACE);
            sqlPartStringBuilder.append(NormalUsedString.LEFT_BRACKET);
            String setColumnValueSql = getInsertSet().values().stream().map(fieldValue -> Objects.requireNonNull(LazySQLUtil.valueToSqlValue(fieldValue)).toString()).collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(setColumnValueSql);
            sqlPartStringBuilder.append(NormalUsedString.RIGHT_BRACKET);
            // insert into %s (%s) VALUES %s  ON DUPLICATE KEY UPDATE
            // TODO 手动添加 ON DUPLICATE KEY UPDATE
            // password=values (password),username=values (username)
            sqlPartStringBuilder.append(NormalUsedString.SPACE);
            sqlPartStringBuilder.append(NormalUsedString.ON_DUPLICATE_KEY_UPDATE);

            String updateColumns = getInsertSet().keySet().stream()
                    .map(column -> column + NormalUsedString.EQUALS + NormalUsedString.VALUES
                            + NormalUsedString.SPACE + NormalUsedString.LEFT_BRACKET + column +
                            NormalUsedString.RIGHT_BRACKET).
                    collect(Collectors.joining(NormalUsedString.COMMA));
            sqlPartStringBuilder.append(updateColumns);
            sqlPartStringBuilder.append(NormalUsedString.SPACE);
        }

        return sqlPartStringBuilder;
    }

    /**
     * 获取删除片段
     *
     * @return
     */
    @Override
    public SqlPartStringBuilder delete() {
        // delete from
        AssertFactory.isFalse(Persistence.ExecutionEnum.DELETE.equals(getExecutionEnum()), "执行方法错误：{" + getExecutionEnum() + "}");
        SqlPartStringBuilder sqlPartStringBuilder = new SqlPartStringBuilder();
        sqlPartStringBuilder.append(Persistence.ExecutionEnum.DELETE.getExecution())
                .append(NormalUsedString.FROM)
                .append(getPrimaryTable());

        return sqlPartStringBuilder;
    }
}
